package org.lc.stk.service.course.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.lc.stk.dao.course.ChapterRepository;
import org.lc.stk.dao.course.CourseRepository;
import org.lc.stk.dao.course.KnowledgePointRepository;
import org.lc.stk.model.course.Chapter;
import org.lc.stk.model.course.Course;
import org.lc.stk.model.course.KnowledgePoint;
import org.lc.stk.service.course.KnowledgePointService;
import org.lc.stk.service.exception.BusinessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

@Service
@Transactional(readOnly = true)
public class KnowledgePointServiceImpl implements KnowledgePointService {

    private final KnowledgePointRepository knowledgePointRepository;
    private final CourseRepository courseRepository;
    private final ChapterRepository chapterRepository;

    public KnowledgePointServiceImpl(KnowledgePointRepository knowledgePointRepository,
                                   CourseRepository courseRepository,
                                   ChapterRepository chapterRepository) {
        this.knowledgePointRepository = knowledgePointRepository;
        this.courseRepository = courseRepository;
        this.chapterRepository = chapterRepository;
    }

    @Override
    @Transactional
    public KnowledgePoint createKnowledgePoint(Integer courseId, Integer chapterId,
                                             KnowledgePoint knowledgePoint, List<Integer> preKnowledgeIds) {
        // 验证课程
        Course course = courseRepository.findById(courseId)
                .orElseThrow(() -> new BusinessException("课程不存在: " + courseId));

        // 验证章节
        Chapter chapter = chapterRepository.findById(chapterId)
                .orElseThrow(() -> new BusinessException("章节不存在: " + chapterId));

        // 验证章节是否属于指定课程
        if (!chapter.getCourse().getId().equals(courseId)) {
            throw new BusinessException("章节不属于指定课程");
        }

        // 验证基本信息
        validateKnowledgePoint(knowledgePoint);

        // 设置关联关系
        knowledgePoint.setCourse(course);
        knowledgePoint.setChapter(chapter);

        // 保存知识点
        knowledgePoint = knowledgePointRepository.save(knowledgePoint);

        // 设置前置知识点
        if (!CollectionUtils.isEmpty(preKnowledgeIds)) {
            setPreKnowledgePoints(knowledgePoint, preKnowledgeIds);
        }

        return knowledgePoint;
    }

    @Override
    @Transactional
    public KnowledgePoint updateKnowledgePoint(Integer id, KnowledgePoint knowledgePoint,
                                             List<Integer> preKnowledgeIds) {
        KnowledgePoint existingKnowledgePoint = knowledgePointRepository.findById(id)
                .orElseThrow(() -> new BusinessException("知识点不存在: " + id));

        // 验证基本信息
        validateKnowledgePoint(knowledgePoint);

        // 更新基本信息
        existingKnowledgePoint.setName(knowledgePoint.getName());

        // 更新前置知识点
        if (preKnowledgeIds != null) {
            if (hasCircularDependency(id, preKnowledgeIds)) {
                throw new BusinessException("存在循环依赖关系");
            }
            setPreKnowledgePoints(existingKnowledgePoint, preKnowledgeIds);
        }

        return knowledgePointRepository.save(existingKnowledgePoint);
    }

    @Override
    @Transactional
    public void deleteKnowledgePoint(Integer id) {
        if (!canDelete(id)) {
            throw new BusinessException("该知识点作为其他知识点的前置知识点，无法删除");
        }
        knowledgePointRepository.deleteById(id);
    }

    @Override
    public Optional<KnowledgePoint> getKnowledgePointById(Integer id) {
        return knowledgePointRepository.findById(id);
    }

    @Override
    public List<KnowledgePoint> getKnowledgePointsByCourse(Integer courseId) {
        return knowledgePointRepository.findByCourseId(courseId);
    }

    @Override
    public List<KnowledgePoint> getKnowledgePointsByChapter(Integer chapterId) {
        return knowledgePointRepository.findByChapterId(chapterId);
    }

    @Override
    public Page<KnowledgePoint> searchKnowledgePoints(Integer courseId, String name, Pageable pageable) {
        return knowledgePointRepository.findByCourseIdAndNameContaining(courseId, name, pageable);
    }

    @Override
    public List<KnowledgePoint> getPreKnowledgePoints(Integer id) {
        return knowledgePointRepository.findPreKnowledgePoints(id);
    }

    @Override
    public List<KnowledgePoint> getNextKnowledgePoints(Integer id) {
        return knowledgePointRepository.findNextKnowledgePoints(id);
    }

    @Override
    public boolean hasCircularDependency(Integer knowledgePointId, List<Integer> preKnowledgeIds) {
        if (CollectionUtils.isEmpty(preKnowledgeIds)) {
            return false;
        }

        // 检查是否将当前知识点作为前置知识点
        if (preKnowledgeIds.contains(knowledgePointId)) {
            return true;
        }

        // 检查前置知识点的前置知识点
        for (Integer preId : preKnowledgeIds) {
            Set<Integer> visited = new HashSet<>();
            visited.add(knowledgePointId);
            if (hasCircularDependencyRecursive(preId, visited)) {
                return true;
            }
        }

        return false;
    }

    @Override
    @Transactional
    public KnowledgePoint moveKnowledgePoint(Integer id, Integer targetChapterId) {
        KnowledgePoint knowledgePoint = knowledgePointRepository.findById(id)
                .orElseThrow(() -> new BusinessException("知识点不存在: " + id));

        Chapter targetChapter = chapterRepository.findById(targetChapterId)
                .orElseThrow(() -> new BusinessException("目标章节不存在: " + targetChapterId));

        // 验证目标章节是否属于同一课程
        if (!targetChapter.getCourse().getId().equals(knowledgePoint.getCourse().getId())) {
            throw new BusinessException("目标章节不属于同一课程");
        }

        knowledgePoint.setChapter(targetChapter);
        return knowledgePointRepository.save(knowledgePoint);
    }

    @Override
    public boolean canDelete(Integer id) {
        return !knowledgePointRepository.isPreKnowledgePoint(id);
    }

    @Override
    @Transactional
    public KnowledgePoint copyKnowledgePoint(Integer id, Integer targetChapterId) {
        KnowledgePoint source = knowledgePointRepository.findById(id)
                .orElseThrow(() -> new BusinessException("知识点不存在: " + id));

        Chapter targetChapter = chapterRepository.findById(targetChapterId)
                .orElseThrow(() -> new BusinessException("目标章节不存在: " + targetChapterId));

        // 验证目标章节是否属于同一课程
        if (!targetChapter.getCourse().getId().equals(source.getCourse().getId())) {
            throw new BusinessException("目标章节不属于同一课程");
        }

        // 创建新的知识点
        KnowledgePoint copy = new KnowledgePoint();
        copy.setName(source.getName() + " - 副本");
        copy.setCourse(source.getCourse());
        copy.setChapter(targetChapter);

        // 保存新知识点
        copy = knowledgePointRepository.save(copy);

        // 复制前置知识点关系
        if (!CollectionUtils.isEmpty(source.getPreKnowledges())) {
            List<Integer> preIds = source.getPreKnowledges().stream()
                    .map(KnowledgePoint::getId)
                    .toList();
            setPreKnowledgePoints(copy, preIds);
        }

        return copy;
    }

    private void validateKnowledgePoint(KnowledgePoint knowledgePoint) {
        if (!StringUtils.hasText(knowledgePoint.getName())) {
            throw new BusinessException("知识点名称不能为空");
        }
    }

    private void setPreKnowledgePoints(KnowledgePoint knowledgePoint, List<Integer> preKnowledgeIds) {
        List<KnowledgePoint> preKnowledges = knowledgePointRepository.findAllById(preKnowledgeIds);

        // 验证所有前置知识点是否存在且属于同一课程
        if (preKnowledges.size() != preKnowledgeIds.size()) {
            throw new BusinessException("部分前置知识点不存在");
        }

        for (KnowledgePoint pre : preKnowledges) {
            if (!pre.getCourse().getId().equals(knowledgePoint.getCourse().getId())) {
                throw new BusinessException("前置知识点必须属于同一课程");
            }
        }

        knowledgePoint.setPreKnowledges(preKnowledges);
    }

    private boolean hasCircularDependencyRecursive(Integer knowledgePointId, Set<Integer> visited) {
        if (visited.contains(knowledgePointId)) {
            return true;
        }

        visited.add(knowledgePointId);
        List<KnowledgePoint> preKnowledges = getPreKnowledgePoints(knowledgePointId);

        for (KnowledgePoint pre : preKnowledges) {
            if (hasCircularDependencyRecursive(pre.getId(), new HashSet<>(visited))) {
                return true;
            }
        }

        return false;
    }
}